#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/time.h>   // 定时器相关函数和结构体

#define ROW 29          // 游戏区行数
#define COL 20          // 游戏区列数
#define RIGHT_COL 6     // 右侧区域的宽度
#define SPEED 700 // 速度

#define DOWN 80         // 方向键：下
#define LEFT 75         // 方向键：左
#define RIGHT 77        // 方向键：右
#define SPACE 32        // 空格键
#define ESC 27          // Esc键

struct block_st {
    int space[4][4];
} block[7][4];   // 用于存储7种基本形状方块的各自的4种形态的信息，共28种

int map[ROW][COL] = {0}; // 游戏地图，数值可以表示不同的颜色
int score = 0;           // 游戏分数
int cur = 0, next = 6;   // 当前方块和下一个方块
int shape_x = 5, shape_y = 1; // 当前方块的位置
int cur_form = 2; // 当前方块的形态


void init_map();
void show_point(int x, int y, int bright, int color);
void init_block_info();
void game_handler(int sig);
void create_next_block();
void show_map();
void show_shape();
void clear_shape();
int move_shape();
int check_down();
void move_down();
void move_left();
void move_right();
void move_revolve();
void clear_input_buffer();
void restore_stdin_blocking();


int main(void)
{
    char c;
    srand(time(NULL));
    system("stty -icanon");		  // 关缓冲，输入的字符会被立即传递给程序
    system("stty -echo");		  // 关回显，用户输入的字符将不会在屏幕上显示
    fprintf(stdout,"\033[?25l");  // 关闭光标显示
    fcntl(0, F_SETFL, O_NONBLOCK);  // 设置标准输入为非阻塞模式

    init_map();
    init_block_info();
    show_map();  // 调用实际的显示函数
    show_shape();

    struct itimerval timer = {{SPEED / 1000, 1000 * (SPEED % 1000)}, {SPEED / 1000, 1000 * (SPEED % 1000)}};
    setitimer(ITIMER_REAL, &timer, NULL);    
    signal(SIGALRM, game_handler);//中断信号 每1s进入一次show_map函数

    while (1) {
        create_next_block();

//        show_shape();
        usleep(100 * SPEED);
        if (!(read(0, &c, 1) > 0)) // 使用 read 函数进行非阻塞读取
            continue;

        if (27 == c) { // ESC结束游戏
           // clear_shape();
            break;
        } else if ('w' == c) { // 旋转方块
        
        } else if ('a' == c) { // 左移方块
            move_left();
            show_shape();
        } else if ('s' == c) { // 下移方块
        
        } else if ('d' == c) { // 右移方块
            move_right();
            show_shape();
        }
        clear_input_buffer();
    }


    printf("\033[%d;%dH", ROW + 1, 1);           //光标位置
    printf("\033[0m");  // 清除所有格式和颜色
    system("stty icanon");		  
    system("stty echo");		  
    fprintf(stdout,"\033[?25h");  
    clear_input_buffer();  // 清空输入缓冲区
    restore_stdin_blocking();
    
    return 0;
}

void init_map()
{
    int i;
    for (i = 0; i < ROW; i++) 
        map[i][0] = map[i][COL - 1] = 1;
    for (i = 0; i < COL; i++)
        map[0][i] = map[ROW - 1][i] = 1;
    for (i = 0; i < ROW; i++)
        map[i][COL - RIGHT_COL - 2] = 1;
}


void game_handler(int sig)
{
    move_down();
    show_map();  // 调用实际的显示函数
   // show_shape();
}

void show_map()
{
    int i, j;
    system("clear");
    for(i = 0; i < ROW; i++) {
        for(j = 0;j < COL; j++) {
            if (map[i][j]) {
                show_point(j, i, 1, 32 + map[i][j]);   //行标i，列表j与坐标系横纵坐标的值是相反的
            }
        }
    } 

    printf("\033[2;%dH", 2 * (COL - RIGHT_COL) - 1);          //   \033[X;XH  光标移动格式
    printf("\033[31mNext:"); 

    printf("\033[11;%dH", 2 * (COL - RIGHT_COL) - 1); 
    printf("\033[32mScore:");   

    printf("\033[12;%dH", 2 * (COL - RIGHT_COL) - 1); 
    printf("\033[32m%-5d", score);   
    fflush(stdout);  //清理缓存，使画面动作流畅
}


/**
 * @brief 输出一个方块
 *
 * @param x 光标横轴位置
 * @param y 光标纵轴位置
 * @param bright 颜色亮度
 * @param color   颜色种类
 */
void show_point(int x, int y, int bright, int color)
{
    printf("\033[%d;%dH", y + 1, x * 2 + 1);           //光标位置
    printf("\033[%d;%dm■ \033[0m", bright, color);       //颜色深浅，颜色种类，注意字符 ■ 后面还有一个空格，所以上方x * 2
}


void create_next_block()
{
    int i, j;
    for (i = 0; i < 4; i++) {
        for (j = 0; j < 4; j++) {
            if (block[next][0].space[i][j] == 0) 
                continue;
            map[2 + i][COL - RIGHT_COL + j] = block[next][0].space[i][j] + 1;
        }
    }
}

//初始化方块信息
void init_block_info()
{
    int i, j, temp[4][4];
    int shape, form;

    //“T”形
    for (i = 0; i <= 2; i++)
        block[0][0].space[1][i] = 1;
    block[0][0].space[2][1] = 1;

    //“L”形
    for (i = 1; i <= 3; i++)
        block[1][0].space[i][1] = 1;
    block[1][0].space[3][2] = 1;

    //“J”形
    for (i = 1; i <= 3; i++)
        block[2][0].space[i][2] = 1;
    block[2][0].space[3][1] = 1;

    for (i = 0; i <= 1; i++) {
        //“Z”形
        block[3][0].space[1][i] = 1;
        block[3][0].space[2][i + 1] = 1;
        //“S”形
        block[4][0].space[1][i + 1] = 1;
        block[4][0].space[2][i] = 1;
        //“O”形
        block[5][0].space[1][i + 1] = 1;
        block[5][0].space[2][i + 1] = 1;
    }

    //“I”形
    for (i = 0; i <= 3; i++)
        block[6][0].space[i][1] = 1;

    for (shape = 0; shape < 7; shape++) { //7种形状	
        for (form = 0; form < 3; form++) { //4种形态（已经有了一种，这里每个还需增加3种）
            //获取第form种形态
            for (i = 0; i < 4; i++) {
                for (j = 0; j < 4; j++)
                    temp[i][j] = block[shape][form].space[i][j];
            }
            //将第form种形态顺时针旋转，得到第form+1种形态
            for (i = 0; i < 4; i++) {
                for (j = 0; j < 4; j++)
                    block[shape][form + 1].space[i][j] = temp[3 - j][i];
            }
        }
    }
}

/**
 * @brief  显示正在下降的图形
 */
void show_shape()
{
    int i, j, flag = 0;
    for (i = 0; i < 4; i++) { // 检查第一行是否有方块
        if (block[cur][cur_form].space[0][i]) {
            flag = 1;
            break;
        }
    }

    for (i = 0; i < 4; i++) {     //显示正在下降的图形
        for (j = 0; j < 4; j++) {
            if (block[cur][cur_form].space[i][j])
                show_point(shape_x + j, shape_y + i - (!flag), 1, 34 + block[cur][cur_form].space[i][j]);
        }
    }
}

void clear_shape()
{
    int i, j;
    for (i = 0; i < 4; i++) {     //清除正在下降的图形
        for (j = 0; j < 4; j++) {
            if (shape_y + i == 0 || shape_x + j == 0)
                continue;
            
            printf("\033[%d;%dH", shape_y + i + 1, (shape_x + j) * 2 + 1);
            printf("\033[0m  \033[0m");     
        }
    }
}

int move_shape()
{



}

int check_down()
{
    int i, j, di, dj;
    for (i = 0; i < 4; i++) {
        for (j = 0; j < 4; j++) {
            if (block[cur][cur_form].space[i][j]) {
                di = shape_y + i;
                dj = shape_x + j - 1;
                if (di >= ROW - 1 || map[di][dj])
                    return 0;
            }
        }
    }

    return 1;
}



void move_down()
{
    if (!check_down())
        return;
    clear_shape();
    shape_y++;
    show_shape();
}

void move_left()
{
    int i, j, di, dj, k = 0; // k表示前k列是空的
    int flag = 1;
    for (i = 0; i < 4; i++) {
        for (j = 0; j < 4; j++) {
            if (block[cur][cur_form].space[j][i])
                flag = 0;
        }
        if (flag == 0)
            break;
        k++;
    }

    for (i = 0; i < 4; i++) {
        for (j = 0; j < 4; j++) {
            if (block[cur][cur_form].space[i][j]) {
                di = shape_y + i - 1;
                dj = shape_x + j - 1 - 1 + k;
                if (dj < 0 || map[di][dj])
                    return; // 不能左移
            }
        }
    }

    clear_shape();
    shape_x--; // 左移
}

void move_right()
{
    int i, j, di, dj, k = 0; // k表示后k列是空的
    int flag = 1;
    for (i = 3; i >= 0; i--) {
        for (j = 0; j < 4; j++) {
            if (block[cur][cur_form].space[j][i])
                flag = 0;
        }
        if (flag == 0)
            break;
        k++;
    }

    for (i = 0; i < 4; i++) {
        for (j = 0; j < 4; j++) {
            if (block[cur][cur_form].space[i][j]) {
                di = shape_y + i - 1;
                dj = shape_x + j + k;
                if (dj >= COL - RIGHT_COL - 2 || map[di][dj])
                    return; // 不能右移
            }
        }
    }

    clear_shape();
    shape_x++; // 右移
}

void move_revolve()
{

}


void clear_input_buffer()
{
    int ch;
    while ((ch = getchar()) != '\n' && ch != EOF);  // 读取直到遇到换行符或文件结束符
}

void restore_stdin_blocking() 
{
    int flags = fcntl(0, F_GETFL, 0); // 获取当前标准输入的文件描述符状态
    if (flags == -1) {
        perror("fcntl");
        return;
    }

    // 清除 O_NONBLOCK 标志以恢复阻塞模式
    flags &= ~O_NONBLOCK;
    if (fcntl(0, F_SETFL, flags) == -1) {
        perror("fcntl");
    }
}
